#include <defs.h>
#include <io.h>
#include <isr.h>
#include <user.h>
#include <vic.h>
#include <ts7200.h>
#include <syscalls.h>
#include <kernel.h>
#include <debug.h>
#include <uart.h>
#include <timer.h>

void ClockInit() {
  // init Timer3
  timer *const timer3 = (timer*)( TIMER3_BASE );
  timer3->load = 25400;
  timer3->control = ENABLE_MASK | MODE_MASK | CLKSEL_MASK;
}

int Uart1Init() {
  // init UART1
  uart *const uart1 = (uart*)( UART1_BASE );
  uart1->lin_ctrl_mid = 0x0;
  uart1->lin_ctrl_low = 0xbf;
  uart1->lin_ctrl_high |= FEN_MASK;
  
  int c; 
  while (!(uart1->flag & RXFE_MASK)) {
    c = uart1->data;
  }
  
  // set up receive
  uart1->ctrl |= MSIEN_MASK | RIEN_MASK | RTIEN_MASK;
  return SUCCESS;
}

int Uart2Init() {
  // init UART2
  uart *const uart2 = (uart*)( UART2_BASE );
  uart2->lin_ctrl_mid = 0x0;
  uart2->lin_ctrl_low = 0x3;
  uart2->lin_ctrl_high |= FEN_MASK;
  
  // clear junk from receive FIFO
  int c;
  while (!(uart2->flag & RXFE_MASK)) {
    c = uart2->data;
  }

  // set up receive and receive timeout interrupts
  uart2->ctrl |= RIEN_MASK | RTIEN_MASK;
  return SUCCESS;
}

void Initialize( sched *s ) {  
  // install software interrupt handler
  int *swi_handler = (int*)( 0x28 );
  *swi_handler = (int)( &KernelEnterSWI ) + CODE_BASE;

  // install abort handlers
  int *prefetch_abort_handler = (int*)( 0x2c );
  *prefetch_abort_handler = (int)( &AbortEnter ) + CODE_BASE;
  int *data_abort_handler = (int*)( 0x30 );
  *data_abort_handler = (int)( &AbortEnter ) + CODE_BASE;

  // install hardware interrupt handler
  int *irq_handler = (int*)( 0x38 );
  *irq_handler = (int)( &KernelEnterIRQ ) + CODE_BASE;
  
  ClockInit();
  Uart1Init();
  Uart2Init();
  
  // turn on caching
  EnableCache();
  
  vic *const vic2 = (vic*)( VIC2_BASE );
  vic2->int_enable |= TIMER3_IRQ;
  vic2->int_enable |= UART1_IRQ;
  vic2->int_enable |= UART2_IRQ;

  // set up data structures
  int i;
  for (i = 0; i <= MAX_PRI; i++) {
    PqInit( &s->queues[i] );
  }
  for (i = 0; i < EVENT_MAX; i++) {
    PqInit( &s->waiters[i] );
  }
  s->next_task_id = 0;

  // create first user task
  SysCreate( s, NULL, 1, &UserInit, "UserInit" );
}

td* Schedule( sched *s ) {
  int i;
  for (i = MAX_PRI; i >= 0; i--) {
    if ( !PqEmpty( &(s->queues[i]) ) ) break;
  }

  if ( i == -1 ) return NULL;
  td *next_task = PqPopFront( &(s->queues[i]) );
  next_task->state = STATE_ACTIVE;
  return next_task;
}

void Activate( sched *s, td *task, req *request ) {
  if (task->mode == IRQ)
    KernelExitIRQ( task, request );
  else
    KernelExitSWI( task, request );
}

void ISR(sched *s, td *task) {
  // vic *const vic1 = (vic*)( VIC1_BASE );
  vic *const vic2 = (vic*)( VIC2_BASE );
  int status = vic2->irq_status;
  
  if (status & TIMER3_IRQ) {
    // bwprintf(COM2, "Timer\r\n");
    TimerHandler(s, task);
  }
  else if (status & UART1_IRQ) {
    // bwprintf(COM2, "UART1\r\n");
    Uart1Handler(s, task);
  }
  else if (status & UART2_IRQ) {
    // bwprintf(COM2, "UART2\r\n");
    Uart2Handler(s, task);
  }
  else {
    DefaultHandler(s, task);
  }
}

void Handle( sched *s, td *task, req *request ) { 
  if (request->call != SYSCALL_PANIC) {
    MemCopy((char*)&task->request, (char*)request, sizeof(req));
  }
  
  switch ( request->call ) {
    case SYSCALL_CREATE:
		  task->ret = SysCreate( s, task, request->a0, (void (*)())request->a1, (char*)request->a2 );
		  break;
    case SYSCALL_MY_TID:
		  task->ret = SysMyTid( s, task );
		  break;
    case SYSCALL_MY_PARENT_TID:
		  task->ret = SysMyParentTid( s, task );
		  break;
    case SYSCALL_PASS:
		  SysPass( s, task );
		  break;
    case SYSCALL_EXIT:
		  SysExit( s, task );
		  break;
    case SYSCALL_FINGER_OF_DEATH:
      SysFingerOfDeath( s, task );
      break;
    case SYSCALL_SEND:
      task->ret = SysSend( s, task,
          request->a0,
          (char*)request->a1, request->a2,
          (char*)request->a3, request->a4 );
      break;
    case SYSCALL_RECEIVE:
      task->ret = SysReceive( s, task,
          (int*)request->a0, (char*)request->a1, request->a2 );
      break;
    case SYSCALL_REPLY:
      task->ret = SysReply( s, task,
          request->a0, (char*)request->a1, request->a2 );
      break;
    case SYSCALL_AWAIT_EVENT:
      task->ret = SysAwaitEvent( s, task,
          request->a0, (char*)request->a1, request->a2 );
      break;
    case SYSCALL_NOTIFY_EVENT:
      task->ret = SysNotifyEvent( s, task,
          request->a0, (char*)request->a1, request->a2 );
      break;
    case SYSCALL_PANIC:
      SysPanic(s, task, (char*)request->a0, (char*)request->a1);
      break;
    case HARDWARE_INTERRUPT:
      ISR(s, task);
      break;
    default:    
      PrintAt(1, 24, "Call #%d\r\n", request->call);
      SysPanic(s, task, "Handle", "Unsupported system call issued.");
	    break;
  }
  
  if ( task->state == STATE_READY || task->state == STATE_ACTIVE ) {
    // reinsert at back of ready queue
    task->state = STATE_READY;
    PqPushBack( &(s->queues[task->pri]), task );
  }
}

void Shutdown( sched *s ) {
  // turn off interrupt receiving in VIC
  vic *const vic2 = (vic*)( VIC2_BASE );
  vic2->int_en_clear = 0xf97865fb;

  // turn off UART1 interrupts
  uart *const uart1 = (uart*)( UART1_BASE );
  uart1->ctrl &= ~(RIEN_MASK | RTIEN_MASK | TIEN_MASK | MSIEN_MASK);

  // turn off UART2 interrupts
  uart *const uart2 = (uart*)( UART2_BASE );
  uart2->ctrl &= ~(RIEN_MASK | RTIEN_MASK | TIEN_MASK | MSIEN_MASK);
}

int main() {
  sched scheduler;
  Initialize( &scheduler );
  td *next_task;
  req request;
  for ( ; ; ) {    
    next_task = Schedule( &scheduler );
    if ( next_task == NULL ) break;
    Activate( &scheduler, next_task, &request );
    Handle( &scheduler, next_task, &request );
  }
  Shutdown( &scheduler );
  return 0;
}
